﻿using ProjectResistance;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;

namespace ProjectResistance.Unit.Test
{
    [TestClass()]
    public class UtilityTest
    {
        public TestContext TestContext { get; set; }

        [TestMethod()]
        [Description("Ensures that GetBands returns correct colors for known conditions")]
        public void GetBandsTestPositive()
        {
            BandColor[] actual;
            BandColor[] expected;

            actual = Utility.GetBands((decimal) 0.47, ToleranceValues.TwoPercent);
            Assert.AreEqual<int>(4, actual.Length, "Incorrect number of bands returned");
            for (int i = 0; i < actual.Length; i++)
            {
                expected = new BandColor[] { BandColor.Yellow, BandColor.Purple, BandColor.Silver, (BandColor)ToleranceValues.TwoPercent };
                Assert.AreEqual<BandColor>(expected[i], actual[i], "failed on 11");
            }

            actual = Utility.GetBands((decimal)2.6, ToleranceValues.TwoPercent);
            Assert.AreEqual<int>(4, actual.Length, "Incorrect number of bands returned");
            for (int i = 0; i < actual.Length; i++)
            {
                expected = new BandColor[] { BandColor.Red, BandColor.Blue, BandColor.Gold, (BandColor)ToleranceValues.TwoPercent };
                Assert.AreEqual<BandColor>(expected[i], actual[i], "failed on 11");
            }

            actual = Utility.GetBands(11, ToleranceValues.TwoPercent);
            Assert.AreEqual<int>(4, actual.Length, "Incorrect number of bands returned");
            for (int i = 0; i < actual.Length; i++)
            {
                expected = new BandColor[] { BandColor.Brown, BandColor.Brown, BandColor.Black, (BandColor)ToleranceValues.TwoPercent };
                Assert.AreEqual<BandColor>(expected[i], actual[i], "failed on 11");
            }

            actual = Utility.GetBands(110, ToleranceValues.TwoPercent);
            Assert.AreEqual<int>(4, actual.Length, "Incorrect number of bands returned");
            for (int i = 0; i < actual.Length; i++)
            {
                // 110
                expected = new BandColor[] { BandColor.Brown, BandColor.Brown, BandColor.Brown, (BandColor)ToleranceValues.TwoPercent };
                Assert.AreEqual<BandColor>(expected[i], actual[i], "failed on 110");
            }

            actual = Utility.GetBands(230, ToleranceValues.TwoPercent);
            Assert.AreEqual<int>(4, actual.Length, "Incorrect number of bands returned");
            for (int i = 0; i < actual.Length; i++)
            {
                // 230
                expected = new BandColor[] { BandColor.Red, BandColor.Orange, BandColor.Brown, (BandColor)ToleranceValues.TwoPercent };
                Assert.AreEqual<BandColor>(expected[i], actual[i], "failed on 230");
            }

            actual = Utility.GetBands(470, ToleranceValues.TwoPercent);
            Assert.AreEqual<int>(4, actual.Length, "Incorrect number of bands returned");
            for (int i = 0; i < actual.Length; i++)
            {
                // 470
                expected = new BandColor[] { BandColor.Yellow, BandColor.Purple, BandColor.Brown, (BandColor)ToleranceValues.TwoPercent };
                Assert.AreEqual<BandColor>(expected[i], actual[i], "failed on 470");
            }

            actual = Utility.GetBands(680, ToleranceValues.TwoPercent);
            Assert.AreEqual<int>(4, actual.Length, "Incorrect number of bands returned");
            for (int i = 0; i < actual.Length; i++)
            {
                // 680
                expected = new BandColor[] { BandColor.Blue, BandColor.Grey, BandColor.Brown, (BandColor)ToleranceValues.TwoPercent };
                Assert.AreEqual<BandColor>(expected[i], actual[i], "failed on 680");
            }

            actual = Utility.GetBands(1000, ToleranceValues.TwoPercent);
            Assert.AreEqual<int>(4, actual.Length, "Incorrect number of bands returned");
            for (int i = 0; i < actual.Length; i++)
            {
                // 1k
                expected = new BandColor[] { BandColor.Brown, BandColor.Black, BandColor.Red, (BandColor)ToleranceValues.TwoPercent };
                Assert.AreEqual<BandColor>(expected[i], actual[i], "failed on 1k");
            }

            actual = Utility.GetBands(1100, ToleranceValues.TwoPercent);
            Assert.AreEqual<int>(4, actual.Length, "Incorrect number of bands returned");
            for (int i = 0; i < actual.Length; i++)
            {
                // 1.1k
                expected = new BandColor[] { BandColor.Brown, BandColor.Brown, BandColor.Red, (BandColor)ToleranceValues.TwoPercent };
                Assert.AreEqual<BandColor>(expected[i], actual[i], "failed on 1.1k");
            }

            actual = Utility.GetBands(56000, ToleranceValues.TwoPercent);
            Assert.AreEqual<int>(4, actual.Length, "Incorrect number of bands returned");
            for (int i = 0; i < actual.Length; i++)
            {
                // 56k
                expected = new BandColor[] { BandColor.Green, BandColor.Blue, BandColor.Orange, (BandColor)ToleranceValues.TwoPercent };
                Assert.AreEqual<BandColor>(expected[i], actual[i], "failed on 56k");
            }
        }

        [TestMethod()]
        [Description("Ensures that GetBands throws for a negative resistance")]
        [ExpectedException(typeof(ArgumentOutOfRangeException))]
        public void GetBandsNegativeInputTest()
        {
            Utility.GetBands(-1, ToleranceValues.TwoPercent);
        }

        [TestMethod()]
        [Description("Ensures that GetResistance returns the proper value for known-good bands")]
        public void GetResistancePositive()
        {
            BandColor[] bands;
            
            // 11
            bands = new BandColor[] { BandColor.Brown, BandColor.Brown, BandColor.Black };
            Assert.AreEqual<decimal>(11, Utility.GetResistance(bands));

            // 110
            bands = new BandColor[] { BandColor.Brown, BandColor.Brown, BandColor.Brown};
            Assert.AreEqual<decimal>(110, Utility.GetResistance(bands));

            // 230
            bands = new BandColor[] { BandColor.Red, BandColor.Orange, BandColor.Brown };
            Assert.AreEqual<decimal>(230, Utility.GetResistance(bands));

            // 470
            bands = new BandColor[] { BandColor.Yellow, BandColor.Purple, BandColor.Brown };
            Assert.AreEqual<decimal>(470, Utility.GetResistance(bands));

            // 680
            bands = new BandColor[] { BandColor.Blue, BandColor.Grey, BandColor.Brown };
            Assert.AreEqual<decimal>(680, Utility.GetResistance(bands));

            // 1k
            bands = new BandColor[] { BandColor.Black, BandColor.Brown, BandColor.Orange };
            Assert.AreEqual<decimal>(1000, Utility.GetResistance(bands));

            // 1.1k
            bands = new BandColor[] { BandColor.Brown, BandColor.Brown, BandColor.Red };
            Assert.AreEqual<decimal>(1100, Utility.GetResistance(bands));

            // 56k
            bands = new BandColor[] { BandColor.Green, BandColor.Blue, BandColor.Orange };
            Assert.AreEqual<decimal>(56000, Utility.GetResistance(bands));
        }

        [TestMethod()]
        [Description("Ensures that we can start with a resistance, convert to bands and back correctly (and vice-versa")]
        public void GetResistanceRoundTrip()
        {
            BandColor[] expected = new BandColor[] { BandColor.Blue, BandColor.Grey, BandColor.Brown, (BandColor)ToleranceValues.FivePercent };
            decimal resistance = Utility.GetResistance(expected);
            BandColor[] actual = Utility.GetBands(resistance, ToleranceValues.FivePercent);
            Assert.AreEqual<int>(expected.Length, actual.Length);
            for(int i = 0 ; i < actual.Length ; i++)
            {
                Assert.AreEqual<BandColor>(expected[i], actual[i]);
            }            
        }

        [TestMethod()]
        [Description("Ensures that GetResistance throws for null input")]
        [ExpectedException(typeof(ArgumentNullException))]
        public void GetResistanceNullInputTest()
        {
            decimal resistance = Utility.GetResistance(null);
        }

        [TestMethod()]
        [Description("Ensures that GetResistance throws for incorrect input length ( < 3 || > 4)")]
        public void GetResistanceIncorrectInputTest()
        {
            ArgumentOutOfRangeException expected = null;

            BandColor[] bands = new BandColor[2];
            try
            {
                decimal resistance = Utility.GetResistance(bands);
            }
            catch(ArgumentOutOfRangeException ex)
            {
                expected = ex;
            }

            Assert.IsNotNull(expected, "Too short did not throw");
            expected = null;

            bands = new BandColor[5];
            try
            {
                decimal resistance = Utility.GetResistance(bands);
            }
            catch (ArgumentOutOfRangeException ex)
            {
                expected = ex;
            }

            Assert.IsNotNull(expected, "Too long did not throw");
        }

        [TestMethod()]
        [Description("Ensures that GetTolerance returns the proper value for known-good bands")]
        public void GetTolerancePositive()
        {
            Assert.AreEqual<ToleranceValues>(ToleranceValues.OnePercent, Utility.GetTolerance(BandColor.Brown), "Brown failure");
            Assert.AreEqual<ToleranceValues>(ToleranceValues.TwoPercent, Utility.GetTolerance(BandColor.Red), "Red failure");
            Assert.AreEqual<ToleranceValues>(ToleranceValues.PointFivePercent, Utility.GetTolerance(BandColor.Green), "Green failure");
            Assert.AreEqual<ToleranceValues>(ToleranceValues.PointTwoFivePercent, Utility.GetTolerance(BandColor.Blue), "Blue failure");
            Assert.AreEqual<ToleranceValues>(ToleranceValues.PointOnePercent, Utility.GetTolerance(BandColor.Purple), "Purple failure");
            Assert.AreEqual<ToleranceValues>(ToleranceValues.PointZeroFivePercent, Utility.GetTolerance(BandColor.Grey), "Grey failure");
            Assert.AreEqual<ToleranceValues>(ToleranceValues.FivePercent, Utility.GetTolerance(BandColor.Gold), "Gold failure");
            Assert.AreEqual<ToleranceValues>(ToleranceValues.TenPercent, Utility.GetTolerance(BandColor.Silver), "Silver failure");
            Assert.AreEqual<ToleranceValues>(ToleranceValues.TwentyPercent, Utility.GetTolerance(BandColor.None), "None failure");
        }

        [TestMethod()]
        [Description("Ensures that GetToleranceColor returns the proper value for known-good bands")]
        public void GetToleranceColorPositive()
        {
            Assert.AreEqual<BandColor>(BandColor.Brown, Utility.GetToleranceColor(ToleranceValues.OnePercent), "Brown failure");
            Assert.AreEqual<BandColor>(BandColor.Red, Utility.GetToleranceColor(ToleranceValues.TwoPercent), "Red failure");
            Assert.AreEqual<BandColor>(BandColor.Green, Utility.GetToleranceColor(ToleranceValues.PointFivePercent), "Green failure");
            Assert.AreEqual<BandColor>(BandColor.Blue, Utility.GetToleranceColor(ToleranceValues.PointTwoFivePercent), "Blue failure");
            Assert.AreEqual<BandColor>(BandColor.Purple, Utility.GetToleranceColor(ToleranceValues.PointOnePercent), "Purple failure");
            Assert.AreEqual<BandColor>(BandColor.Grey, Utility.GetToleranceColor(ToleranceValues.PointZeroFivePercent), "Grey failure");
            Assert.AreEqual<BandColor>(BandColor.Gold, Utility.GetToleranceColor(ToleranceValues.FivePercent), "Gold failure");
            Assert.AreEqual<BandColor>(BandColor.Silver, Utility.GetToleranceColor(ToleranceValues.TenPercent), "Silver failure");
            Assert.AreEqual<BandColor>(BandColor.None, Utility.GetToleranceColor(ToleranceValues.TwentyPercent), "None failure");
        }

        [TestMethod()]
        [Description("Ensures that ResistanceUp works for digit 1")]
        public void ResistanceUpDigit1()
        {
            Assert.AreEqual<decimal>(200, Utility.ResistanceUp(100, 1));
            Assert.AreEqual<decimal>(900, Utility.ResistanceUp(800, 1));
            Assert.AreEqual<decimal>(000, Utility.ResistanceUp(900, 1));
            Assert.AreEqual<decimal>(30, Utility.ResistanceUp(20, 1));
            Assert.AreEqual<decimal>(910, Utility.ResistanceUp(810, 1));
            Assert.AreEqual<decimal>(590, Utility.ResistanceUp(490, 1));
            Assert.AreEqual<decimal>(4200, Utility.ResistanceUp(3200, 1));
            Assert.AreEqual<decimal>(0700, Utility.ResistanceUp(9700, 1));
        }

        [TestMethod()]
        [Description("Ensures that ResistanceUp works for digit 2")]
        public void ResistanceUpDigit2()
        {
            Assert.AreEqual<decimal>(110, Utility.ResistanceUp(100, 2));
            Assert.AreEqual<decimal>(790, Utility.ResistanceUp(780, 2));
            Assert.AreEqual<decimal>(100, Utility.ResistanceUp(190, 2));
            Assert.AreEqual<decimal>(21, Utility.ResistanceUp(20, 2));
            Assert.AreEqual<decimal>(5700, Utility.ResistanceUp(5600, 2));
            Assert.AreEqual<decimal>(33000, Utility.ResistanceUp(32000, 2));
            Assert.AreEqual<decimal>(7000, Utility.ResistanceUp(7900, 2));
        }

        [TestMethod()]
        [Description("Ensures that ResistanceUp works for multiplier")]
        public void ResistanceUpDigit3()
        {
            Assert.AreEqual<decimal>(1000, Utility.ResistanceUp(100, 3));
            Assert.AreEqual<decimal>(320, Utility.ResistanceUp(32, 3));
            Assert.AreEqual<decimal>(30, Utility.ResistanceUp(3, 3));
            Assert.AreEqual<decimal>(8900, Utility.ResistanceUp(890, 3));
            Assert.AreEqual<decimal>(56000, Utility.ResistanceUp(5600, 3));
        }

        [TestMethod()]
        [Description("Ensures that ResistanceDown works for digit 1")]
        public void ResistanceDownDigit1()
        {
            Assert.AreEqual<decimal>(000, Utility.ResistanceDown(100, 1));
            Assert.AreEqual<decimal>(700, Utility.ResistanceDown(800, 1));
            Assert.AreEqual<decimal>(60, Utility.ResistanceDown(70, 1));
            Assert.AreEqual<decimal>(4200, Utility.ResistanceDown(3200, 1));
            Assert.AreEqual<decimal>(0700, Utility.ResistanceDown(9700, 1));
        }

        [TestMethod()]
        [Description("Ensures that ResistanceDown works for digit 2")]
        public void ResistanceDownDigit2()
        {
            Assert.AreEqual<decimal>(190, Utility.ResistanceDown(100, 2));
            Assert.AreEqual<decimal>(310, Utility.ResistanceDown(320, 2));
            Assert.AreEqual<decimal>(45, Utility.ResistanceDown(46, 2));
            Assert.AreEqual<decimal>(5500, Utility.ResistanceDown(5600, 2));
            Assert.AreEqual<decimal>(75000, Utility.ResistanceDown(76000, 2));
            Assert.AreEqual<decimal>(39000, Utility.ResistanceDown(30000, 2));
        }

        [TestMethod()]
        [Description("Ensures that ResistanceDown works for multiplier")]
        public void ResistanceDownDigit3()
        {
            Assert.AreEqual<decimal>(10, Utility.ResistanceDown(100, 3));
            Assert.AreEqual<decimal>(32, Utility.ResistanceDown(320, 3));
            Assert.AreEqual<decimal>((decimal)4.6, Utility.ResistanceDown(46, 3));
            Assert.AreEqual<decimal>(560, Utility.ResistanceDown(5600, 3));
            Assert.AreEqual<decimal>(7600, Utility.ResistanceDown(76000, 3));
        }
    }
}
